/**
 * 说明：计算多个区间的交集。
 *   区间用长度为2的数字数组表示，如[2, 5]表示区间2到5（包括2和5）；
 *   区间不限定方向，如[5, 2]等同于[2, 5]；
 *   实现`getIntersection`，可接收多个区间，并返回所有区间的交集（用区间表示），如空集用null表示
 * 示例：
 *   getIntersection([5, 2], [4, 9], [3, 6]); // [4, 5]
 *   getIntersection([1, 7], [8, 9]); // null
 */
function getIntersection(arr) {
  /* 代码实现 */

  // 使用模拟的方式解决
  let index = [];

  // 采用数组记录的方式
  for (let i = 0; i < arr.length; i++) {
    let maxNum = Math.max(arr[i][0], arr[i][1]); // 取当前区间的最大值
    let minMum = Math.min(arr[i][0], arr[i][1]); // 取当前区间的最小值

    // 记录到index 数组中
    for (let j = minMum; j <= maxNum; j++) {
      if (index[j] === undefined) {
        index[j] = 1;
      } else {
        index[j]++;
      }
    }
  }

  let start, end;

  // 获得交集区间
  for (let i = 0; i < index.length; i++) {
    if (index[i - 1] !== arr.length && index[i] === arr.length) {
      start = i;
    }
    if (index[i] === arr.length && index[i + 1] !== arr.length) {
      end = i;
    }
  }
  // 返回结果
  if (start === undefined && end === undefined) {
    return null;
  } else {
    return [start, end];
  }
}

console.log(
  getIntersection([
    [5, 2],
    [4, 9],
    [3, 6],
  ])
);

/**
 * 实现一个裁剪拼凑数组的函数，可按指定的序号将数组剪裁成两半，并把前后部分互换拼装成新数组
 * 示例：
 *  cutAndJoinArray(['a', 'b', 'c', 'd'], 2); // 返回 ['c', 'd', 'a', 'b']
 *  cutAndJoinArray(['a', 'b', 'c']); // 返回 ['a', 'b', 'c']
 *  cutAndJoinArray(['a', 'b', 'c'], 4); // 返回 ['b', 'c', 'a']
 */
function cutAndJoinArray(arr, index) {
  /* 代码实现 */

  // 如果第2个参数没传进来的话，那么直接返回原数组就可以了
  if (index === undefined) {
    return arr;
  }

  // 如果第2个参数传了进来的话，那么先除以数组长度进行取余,之后再数组切割，然后数组合并
  let arrIndex = index % arr.length;

  // 返回数组切割完的结果
  return arr.slice(arrIndex).concat(arr.slice(0, arrIndex));
}

console.log(cutAndJoinArray(["a", "b", "c", "d"], 2));

/**
 * 字符串隐藏部分内容
 * 说明：实现一个方法，接收一个字符串和一个符号，将字符串中间四位按指定符号隐藏
 *   1. 符号无指定时使用星号（*）
 *   2. 接收的字符串小于或等于四位时，返回同样长度的符号串，等同于全隐藏，如 123，隐藏后是 ***
 *   3. 字符串长度是大于四位的奇数时，如 123456789，隐藏后是 12****789，奇数多出来的一位在末尾
 * 示例：
 *   mask('alibaba', '#');  // a####ba
 *   mask('85022088');   // 85****88
 *   mask('hello');  // ****o
 *   mask('abc', '?');  // ???
 *   mask('阿里巴巴集团', '?'); // 阿????团
 */
function mask(str, char = "*") {
  /* 代码实现 */

  // 如果字符小于等于4的时候，把所有的字符都替换成 char，并进行返回

  if (str.length <= 4) {
    return str
      .split("")
      .map((item) => char)
      .join("");
  }

  // 如果字符串的长度大于4的时候进行处理，奇数和偶数的处理方式一样
  let arr = str.split("");
  let index = Math.floor(arr.length / 2);
  arr[index] = char;
  arr[index + 1] = char;
  arr[index - 1] = char;
  arr[index - 2] = char;
  return arr.join("");
}

console.log(mask("85022088", "#"));

/**
 * 写一个数组比较函数，比较数组 a 和 b 并返回相同和不同元素的个数（数组内元素不重复）。
 * 示例：
 *   compare([1, 3, 4], [3, 4]) // { diff: 1, same: 2 }
 *   compare([1, 3], []) // { diff: 2, same: 0 }
 *   compare([1, 3], [1, 3]) // { diff: 0, same: 2 }
 */
function compare(a, b) {
  /* 代码实现 */
  // 先用个set 把 a 数组的元素存进去，然后遍历b 数组，如果元素能够在set 中找到，这个时候same 的值就加1
  // 然后把元素的值从set 中删除掉，找不到的话diff的值就加1
  //最后diff 的值就算 set 的容量 加上原先的diff

  let same = 0,
    diff = 0;
  let set = new Set();

  a.forEach((item) => {
    set.add(item);
  });

  for (let item of b) {
    if (set.has(item)) {
      set.delete(item);
      same++;
    } else {
      diff++;
    }
  }

  return { same, diff: diff + set.size };
}

console.log(compare([1, 3, 4], [3, 4]));

/**
 * 说明：给定一个字符串，找出不含有重复字符的最长子串的长度
 *   如给定'abcabcbb' ，没有重复字符的最长子串是'abc' ，那么长度就是3
 * 示例：
 *   lengthOfLongestSubstring('bbbbb'); // 返回 1
 *   lengthOfLongestSubstring('')； // 返回 0
 *   lengthOfLongestSubstring('cbca'); // 返回 3
 *   lengthOfLongestSubstring('c'); // 返回 1
 *   lengthOfLongestSubstring('abcdaccbac'); // 返回 4
 */
function lengthOfLongestSubstring(str) {
  /* 代码实现 */
  // 使用滑动窗口解决
  let arr = [];
  let maxValue = 0; // 记录最长子串的长度
  for (let i of str) {
    let index = arr.indexOf(i);
    // 判断当前的字符是不是已经在arr数组中存在,如果不存在，则直接放入数组中，更新当前的最长长度
    if (index === -1) {
      arr.push(i);
      maxValue = Math.max(maxValue, arr.length);
    } else {
      // 如果存在，进行arr数组切割，把index和index之前的字符删除掉
      arr.splice(0, index + 1);
      arr.push(i); // 放入当前元素
      maxValue = Math.max(maxValue, arr.length);
    }
  }

  // 返回最大值
  return maxValue;
}

console.log(lengthOfLongestSubstring("abcdaccbac"));

/**
 * 说明：给定一个 "数字计算表达式”，请按规则解析表达式并计算出最终表达式的值
 * 数字计算表达式规范：
 *   形如@[pattern_name]{[...args]}，称为一个表达式
 *   pattern_name - 为Javascipt 内置对象 Math 的成员方法名
 *   ...args - 为对应Math成员方法对应的参数
 * 示例：
 *   parse(`@pow{@max{1,2,3,4,5},@min{2,7,8}}`);    // 25
 *   parse(`@pow{@ceil{10.5},@min{2,7,8}}`);   // 121
 *   parse(`@pow{@max{1,2,3,4,@pow{2, 3}},@min{2,7,8}}`); // 64
 * 提示：
 *   Math的成员方法：例如浏览器环境下，window.Math.max / window.Math.min 等
 *   尽量选用更优解法，如性能、安全性、通用性等方面
 *   可以使用伪代码作答
 */

function parse(pattern) {
  // 使用2个栈来解析字符串,然后当解析到pow,max,min,ceil 时调用对应的Math 成员返回进行计算
  // 1个栈存放 方法，1 个栈 对应一个二维数组存放数字
  let stack = [];
  let numList = []; // 一个二维数组，对应到栈
  let way; //  记录要执行的Math方法
  let res; // 记录结果

  for (let i = 0; i < pattern.length; i++) {
    if (pattern[i] === ",") {
      continue;
    } else if (pattern[i] === "{") {
      // 方法栈添加方法，把方法保存起来
      // 方法栈对应的数组初始化
      stack.push(way);
      numList.push([]);
    } else if (pattern[i] === "@") {
      // 把方法名重新变成空字符串
      way = "";
    } else if (/[0-9]/.test(pattern[i])) {
      // 提取数字字符串
      let first = i;
      while (pattern[i] !== "," && pattern[i] !== "}") {
        i++;
      }
      let end = i;
      let str = pattern.substr(first, end - first);
      i--; // 注意这里要减回
      numList[numList.length - 1].push(Number(str));
      // 解析成数字，然后存到栈方法对应的数组
      // 记录字符
    } else if (pattern[i] === "}") {
      let mathWay = stack.pop(); // 拿出栈顶的数字方法，然后进行处理
      switch (mathWay) {
        case "max":
          res = Math.max(...numList[numList.length - 1]);
          numList.pop(); // 弹出这个方法对应的数字
          if (numList.length === 0) {
            // 栈空了返回结果
            return res;
          }
          numList[numList.length - 1].push(res);
          break;
        case "min":
          res = Math.min(...numList[numList.length - 1]);
          numList.pop(); // 弹出这个方法对应的数字
          if (numList.length === 0) {
            // 栈空了返回结果
            return res;
          }
          numList[numList.length - 1].push(res);
          break;
        case "pow":
          res = Math.pow(
            numList[numList.length - 1][0],
            numList[numList.length - 1][1]
          );
          numList.pop(); // 弹出这个方法对应的数字
          if (numList.length === 0) {
            // 栈空了返回结果
            return res;
          }
          numList[numList.length - 1].push(res);
          break;
        case "ceil":
          res = Math.ceil(...numList[numList.length - 1]);
          numList.pop(); // 弹出这个方法对应的数字
          if (numList.length === 0) {
            // 栈空了返回结果
            return res;
          }
          numList[numList.length - 1].push(res);
          break;
      }
    } else {
      // 拼接方法
      way = way + pattern[i];
    }
  }
}

console.log(parse(`@pow{@max{1,2,3,4,@pow{2, 3}},@min{2,7,8}}`));

/**
 * 说明：通过编写一个函数 parseCSSString(cssString)  将该 cssString 转化为一个类似于 React Style Object 的对象，如下：
 * 示例：
 *   // 输入
 *   :root {
 *   background: red;
 *   font-size: 12px;
 *   }
 *   // 输出
 *   {
 *   background: 'red',
 *   fontSize: '12px'
 *   }

 *   // 输入
 *   :root >div {
 *   border-left: 2px solid red;
 *   font-size: 13px;
 *   }
 *   // 输出
 *   {
 *   borderSize: '2px solid red',
 *   fontSize: '13px'
 *   }
 */
function parseCSSString(cssString) {
  /* 代码实现 */
  // 把字符提取出来
  let Llist = []; // 存放左边队列的字符
  let Rlist = []; // 存放右边队列的字符
  let jude = true; //  判断是否要将字符放入栈中
  let res = "{";
  let index = cssString.indexOf("{");

  for (let i = index + 1; i < cssString.length; i++) {
    // 后面的字符放入
    if (cssString[i] === ";") {
      jude = true;
      res += "'" + Rlist.join("") + "'";
      Rlist = [];
      res += ",";
    }
    // 后面的字符不用放入栈中
    else if (cssString[i] === ":") {
      jude = false;
      let arr = Llist.join("").split("-");
      res += arr[0];
      if (arr[1] !== undefined) {
        let temp = arr[1].split("");
        temp[0] = temp[0].toUpperCase();
        res += temp.join("");
      }
      Llist = [];
      res += ":";
    } else {
      if (cssString[i] === " " || cssString[i] === "\n") {
        res += cssString[i];
      } else if (jude) {
        Llist.push(cssString[i]);
      } else if (!jude) {
        Rlist.push(cssString[i]);
      }
    }
  }
  return res + "}";
}

console.log(
  parseCSSString(`
     :root {
    background: red;
    font-size: 12px;
    }
`)
);
